package com.mpgame.battle;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicInteger;

import com.mpgame.common.proto.BattleActionReq;
import com.mpgame.common.proto.BattleActionRsp;
import com.mpgame.common.proto.BattleEndMsg;
import com.mpgame.common.proto.BattleMatchReq;
import com.mpgame.common.proto.BattleMatchRsp;
import com.mpgame.common.proto.BattleMonsterInfo;
import com.mpgame.common.proto.BattleSkillInfo;
import com.mpgame.common.proto.BattleStartReq;
import com.mpgame.common.proto.BattleStartRsp;
import com.mpgame.common.proto.BattleType;
import com.mpgame.common.proto.CmdId;
import com.mpgame.common.utils.Log;
import com.mpgame.database.mysql.model.game.MonsterStore;
import com.mpgame.database.mysql.model.game.Skills;
import com.mpgame.database.mysql.services.GameBattleDataSvc;
import com.mpgame.database.mysql.services.GameBattleDataSvc.GameBattleStartData;
import com.mpgame.database.mysql.services.GameBattleDataSvc.GamePVPMatchData;
import com.mpgame.database.redis.utils.PlayerOnlineUtil;
import com.mpgame.common.helper.GrpcStreamHelper;
import com.mpgame.common.mq.KafkaBus;

public class BattleRoomMgr {
    private static volatile BattleRoomMgr instance;
    private final AtomicInteger roomidGenerator = new AtomicInteger(1000);
    /** 映射：roomId -> BattleRoom */
    private final Map<Integer, BattleRoom> rooms = new ConcurrentHashMap<>();
    /** 映射：accountId -> roomId */
    private final Map<Integer, Integer> playerRoomMap = new ConcurrentHashMap<>();
    /** 定时清理超时房间 */
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
    private GrpcStreamHelper streamHelper;

    public void setStreamHelper(GrpcStreamHelper streamHelper) {
        this.streamHelper = streamHelper;
    }

    private BattleRoomMgr() {}

    public static BattleRoomMgr getInstance() {
        if (instance == null) {
            synchronized (BattleRoomMgr.class) {
                if (instance == null) {
                    instance = new BattleRoomMgr();
                }
            }
        }
        return instance;
    }

    /** 请求创建房间 */
    public BattleMatchRsp requestMatch(BattleMatchReq req) {
        try {
            if (req.getBattleType() == BattleType.PVP) {
                return handlePVPMatch(req);
            } else if (req.getBattleType() == BattleType.PVE) {
                return handlePVEMatch(req);
            } else if (req.getBattleType() == BattleType.BOSS) {
                return handleBOSSMatch(req);
            } else {
                return handlePVEMatch(req);
            }
        } catch (Exception e) {
            String errMsg = "处理匹配请求异常: " + e.getMessage();
            Log.err(errMsg);
            return BattleMatchRsp.newBuilder().setErrMsg(errMsg).build();
        }
    }

    /** 请求开始比赛（结束加载界面），返回给请求方战场初始数据 */
    public BattleStartRsp requestStartMatch(BattleStartReq req) {
        try {
            BattleRoom room = getRoom(req.getAccountId());
            if (room == null) {
                String errMsg = "战斗已结束！";
                Log.err(errMsg);
                return BattleStartRsp.newBuilder().setErrMsg(errMsg).build();
            }

            BattleStartRsp.Builder rsp = BattleStartRsp.newBuilder();

            rsp.setAccountId(req.getAccountId());
            rsp.setOpponentId(room.getOpponentAccountId(req.getAccountId()));
            MonsterStore myMonsterStore = room.getMonsterStore(req.getAccountId());
            MonsterStore opponentMonsterStore = room.getOpponentMonsterStore(req.getAccountId());

            GameBattleDataSvc battleDataSvc = new GameBattleDataSvc();
            GameBattleStartData battleStartData = battleDataSvc.getBattleStartData(myMonsterStore,
                    opponentMonsterStore);

            rsp.setMyMonsterInfo(
                    BattleMonsterInfo.newBuilder()
                            .setMonsterId(myMonsterStore.getId())
                            .setName(battleStartData.myMonster.getName())
                            .setFamilyId(battleStartData.myMonster.getFamilyId())
                            .setLevel(myMonsterStore.getLv())
                            .setHealth(myMonsterStore.getHealth())
                            .setMaxHealth(myMonsterStore.getHealth())
                            .setResourceUrl(battleStartData.myMonster.getResourceUrl())
                            .build());
            rsp.setOpponentMonsterInfo(
                    BattleMonsterInfo.newBuilder()
                            .setMonsterId(opponentMonsterStore.getId())
                            .setName(battleStartData.opponentMonster.getName())
                            .setFamilyId(battleStartData.opponentMonster.getFamilyId())
                            .setLevel(opponentMonsterStore.getLv())
                            .setHealth(opponentMonsterStore.getHealth())
                            .setMaxHealth(opponentMonsterStore.getHealth())
                            .setResourceUrl(battleStartData.opponentMonster.getResourceUrl())
                            .build());
            rsp.setSkill1Info(buildBattleSkillInfoFromSkills(battleStartData.mySkill1));
            rsp.setSkill2Info(buildBattleSkillInfoFromSkills(battleStartData.mySkill2));
            rsp.setSkill3Info(buildBattleSkillInfoFromSkills(battleStartData.mySkill3));
            rsp.setSkill4Info(buildBattleSkillInfoFromSkills(battleStartData.mySkill4));
            rsp.setIsLeft(req.getAccountId() == room.getAccount1Id());

            room.setReady(req.getAccountId());
            rsp.setAllReady(room.isAllReady());
            return rsp.build();
        } catch (Exception e) {
            String errMsg = "处理匹配请求异常：" + e.getMessage();
            Log.err(errMsg);
            return BattleStartRsp.newBuilder().setErrMsg(errMsg).build();
        }
    }

    private BattleSkillInfo buildBattleSkillInfoFromSkills(Skills skillInDB) {
        return BattleSkillInfo.newBuilder()
                .setSkillId(skillInDB.getId())
                .setFamilyId(skillInDB.getFamilyId())
                .setName(skillInDB.getName())
                .setPower(skillInDB.getPower())
                .setPp(skillInDB.getPp())
                .setMaxPp(skillInDB.getPp())
                .setPriority(skillInDB.getPriority())
                .setDescription(skillInDB.getDescription())
                .build();
    }

    /** 请求进行战斗操作（使用技能，药水，抓捕等） */
    public BattleActionRsp requestBattleAction(BattleActionReq req) {
        try {
            BattleRoom room = getRoom(req.getAccountId());
            if (room == null) {
                String errMsg = "战斗已结束！";
                Log.err(errMsg);
                return BattleActionRsp.newBuilder().setErrMsg(errMsg).build();
            }
            BattleActionRsp rsp = room.processAction(req);
            if (rsp != null && rsp.getWinnerId() != -1) {
                int winnerId = rsp.getWinnerId();
                int loserId = room.getOpponentAccountId(winnerId);
                KafkaBus.sendMsg(
                        winnerId,
                        winnerId,
                        CmdId.BATTLE_END_MSG,
                        BattleEndMsg.newBuilder()
                                .setIsWinner(true)
                                .setContent("战斗胜利！")
                                .build());
                KafkaBus.sendMsg(
                        loserId,
                        loserId,
                        CmdId.BATTLE_END_MSG,
                        BattleEndMsg.newBuilder()
                                .setIsWinner(false)
                                .setContent("战斗失败！")
                                .build());
                cleanupRoom(room.getRoomId());
            }
            return rsp;
        } catch (Exception e) {
            String errMsg = "处理回合行动异常：" + e.getMessage();
            Log.err(errMsg);
            return BattleActionRsp.newBuilder().setErrMsg(errMsg).build();
        }
    }

    private BattleMatchRsp handlePVPMatch(BattleMatchReq req) {
        Log.info("处理PVP类型对战请求！");

        BattleMatchRsp.Builder rsp = BattleMatchRsp.newBuilder();

        if (!PlayerOnlineUtil.isPlayerOnline(req.getAccountId()) ||
                !PlayerOnlineUtil.isPlayerOnline(req.getOpponentAccountId())) {
            Log.warn("玩家不在线！");
            rsp.setErrMsg("玩家不在线！");
            return rsp.build();
        }
        if (playerRoomMap.containsKey(req.getAccountId()) ||
                playerRoomMap.containsKey(req.getOpponentAccountId())) {
            Log.warn("玩家正在对战中...");
            rsp.setErrMsg("玩家正在对战中...");
            return rsp.build();
        }

        GameBattleDataSvc battleDataService = new GameBattleDataSvc();
        GamePVPMatchData matchData = battleDataService.getPVPMatchData(req.getAccountId(), req.getOpponentAccountId());

        if (matchData.player1Monster == null) {
            Log.warn("您没有可用的精灵");
            rsp.setErrMsg("您没有可用的精灵");
            return rsp.build();
        }
        if (matchData.player2Monster == null) {
            Log.warn("对手没有可用的精灵");
            rsp.setErrMsg("对手没有可用的精灵");
            return rsp.build();
        }

        int roomId = createBattleRoom(
                req.getAccountId(),
                req.getOpponentAccountId(),
                matchData.player1Monster,
                matchData.player2Monster);

        if (roomId == -1) {
            Log.warn("创建房间对局失败！");
            rsp.setErrMsg("创建对局房间失败！");
            return rsp.build();
        }

        rsp.setAccount1Id(req.getAccountId())
                .setAccount2Id(req.getOpponentAccountId());

        Log.info("PVP对战匹配成功：房间ID=" + roomId +
                "，玩家1=" + req.getAccountId() +
                "，玩家2=" + req.getOpponentAccountId());

        return rsp.build();
    }

    private BattleMatchRsp handlePVEMatch(BattleMatchReq req) {
        Log.info("处理PVE类型对战请求！");
        return null;
    }

    private BattleMatchRsp handleBOSSMatch(BattleMatchReq req) {
        Log.info("处理BOSS类型对战请求！");
        return null;
    }

    public int createBattleRoom(int account1Id, int account2Id, MonsterStore monster1,
            MonsterStore monster2) {
        if (playerRoomMap.containsKey(account1Id) || playerRoomMap.containsKey(account2Id))
            return -1;
        int roomId = roomidGenerator.incrementAndGet();
        BattleRoom room = new BattleRoom(roomId, account1Id, account2Id, monster1, monster2);
        rooms.put(roomId, room);
        playerRoomMap.put(account1Id, roomId);
        playerRoomMap.put(account2Id, roomId);
        return roomId;
    }

    public BattleActionRsp processBattleAction(BattleActionReq req) {
        Integer roomId = playerRoomMap.get(req.getAccountId());
        if (roomId == null)
            return null;
        BattleRoom room = rooms.get(roomId);
        if (room == null) {
            playerRoomMap.remove(req.getAccountId());
            return null;
        }
        BattleActionRsp result = room.processAction(req);
        if (room.isBattleEnded())
            cleanupRoom(roomId);
        return result;
    }

    public void handlePlayerOffline(int accountId) {
        Integer roomId = playerRoomMap.get(accountId);
        if (roomId == null)
            return; // 玩家不在任何战斗房间中

        BattleRoom room = rooms.get(roomId);
        if (room == null) {
            playerRoomMap.remove(accountId);
            return;
        }
        room.setPlayerOffline(accountId);
        if (room.hasPlayerOffline()) {
            int onlinePlayerId = room.getOnlinePlayerId();
            if (onlinePlayerId != -1) {// 有在线玩家
                streamHelper.sendStreamPkt(
                        onlinePlayerId,
                        CmdId.BATTLE_END_MSG,
                        BattleEndMsg.newBuilder()
                                .setIsWinner(true)
                                .setContent("对方已下线！")
                                .build());
                Log.info("玩家" + accountId + "在战斗房间" + roomId + "中离线，玩家" + onlinePlayerId + "获得胜利！");
            } else {
                // 均离线，两者均失败
                Log.info("房间" + roomId + "玩家均离线，战斗中止！");
            }
            // 清理房间
            cleanupRoom(roomId);
        }
    }

    /** accountId -> room */
    public BattleRoom getRoom(int accountId) {
        return rooms.getOrDefault(playerRoomMap.getOrDefault(accountId, -1), null);
    }

    private void cleanupRoom(int roomId) {
        Log.info("清理房间：" + roomId);
        BattleRoom room = rooms.remove(roomId);
        if (room != null)
            playerRoomMap.entrySet().removeIf(entry -> entry.getValue().equals(roomId));
    }
}
